package com.sh.data.engine.domain.api.service.impl;

import cn.hutool.core.convert.Convert;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.sh.data.engine.common.enumDefinition.DataApiApproveStatusEnum;
import com.sh.data.engine.common.exception.BusinessException;
import com.sh.data.engine.common.util.ServletUtils;
import com.sh.data.engine.domain.api.model.domain.*;
import com.sh.data.engine.domain.api.model.param.DataApiAuthApproveOrderParam;
import com.sh.data.engine.domain.api.model.param.DataApiAuthApproveParam;
import com.sh.data.engine.domain.api.service.DataApiAuthService;
import com.sh.data.engine.domain.api.service.DataApiRestService;
import com.sh.data.engine.domain.api.service.DataApiService;
import com.sh.data.engine.domain.api.service.DataApiUserStatisticsService;
import com.sh.data.engine.domain.authority.service.TokenService;
import com.sh.data.engine.domain.base.model.UserContext;
import com.sh.data.engine.domain.util.ConvertUtil;
import com.sh.data.engine.domain.workspace.manager.model.domain.UserDomain;
import com.sh.data.engine.domain.workspace.manager.service.UserService;
import com.sh.data.engine.repository.dao.api.DataApiAuthMapper;
import com.sh.data.engine.repository.dao.api.entity.DataApiAuthEntity;
import com.sh.data.engine.repository.dao.api.entity.DataApiEntity;
import com.sh.data.engine.repository.dao.api.entity.DataApiUserStatisticsEntity;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author : fuchen
 * @date : 2022/2/10 14:43
 */
@Service
public class DataApiAuthServiceImpl extends ServiceImpl<DataApiAuthMapper, DataApiAuthEntity>
    implements DataApiAuthService {

    private static final String API_REDIS_KEY_PREFIX = "data_api_";

    @Autowired
    private UserService userService;

    @Autowired
    private DataApiUserStatisticsService dataApiUserStatisticsService;

    @Autowired
    private DataApiService dataApiService;

    @Autowired
    private DataApiRestService dataApiRestService;

    @Autowired
    private TokenService tokenService;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Override
    public Long getTotalApplyByProjectId(Long projectId, Integer status) {
        LambdaQueryWrapper<DataApiAuthEntity> lambdaQueryWrapper =
            new LambdaQueryWrapper<DataApiAuthEntity>().eq(DataApiAuthEntity::getProjectId, projectId);
        if (null != status) {
            lambdaQueryWrapper.eq(DataApiAuthEntity::getApproveStatus, status);
        }
        long count = this.count(lambdaQueryWrapper);
        return count;
    }

    @Override
    public Long getTotalApplyByProjectIdAndUserId(Long projectId, Integer status, String userId) {
        LambdaQueryWrapper<DataApiAuthEntity> lambdaQueryWrapper =
            new LambdaQueryWrapper<DataApiAuthEntity>()
                .eq(DataApiAuthEntity::getProjectId, projectId)
                .eq(DataApiAuthEntity::getUserId, userId);

        if (null != status) {
            lambdaQueryWrapper.eq(DataApiAuthEntity::getApproveStatus, status);
        }
        long count = this.count(lambdaQueryWrapper);
        return count;
    }

    @Override
    public boolean applyApiById(String userId, Long apiId, Long projectId, String username) {

        DataApiEntity dataApiEntity = dataApiService.getById(apiId);
        if (null == dataApiEntity || dataApiEntity.getApiStatus() == 0) {
            throw new BusinessException("该API不存在或者未发布,无法审核");
        }
        // 首先判断该用户是否申请过，如果存在只需要更改状态，否则插入一条记录
        LambdaQueryWrapper<DataApiAuthEntity> lambdaQueryWrapper =
            new LambdaQueryWrapper<DataApiAuthEntity>()
                .eq(DataApiAuthEntity::getApiId, apiId)
                .eq(DataApiAuthEntity::getUserId, userId);

        DataApiAuthEntity dataApiAuthEntity = getOne(lambdaQueryWrapper);
        if (Objects.isNull(dataApiAuthEntity)) {
            dataApiAuthEntity = new DataApiAuthEntity();
            dataApiAuthEntity.setApiId(apiId);
            dataApiAuthEntity.setApiName(dataApiEntity.getApiName());
            dataApiAuthEntity.setApplyTime(new Date());
            dataApiAuthEntity.setUserId(userId);
            dataApiAuthEntity.setCreateTime(new Date());
            dataApiAuthEntity.setProjectId(projectId);
            dataApiAuthEntity.setApproveStatus(DataApiApproveStatusEnum.WAIT_APPROVE.getApproveStatus());
            dataApiAuthEntity.setCreatorId(userId);
            dataApiAuthEntity.setUsername(username);
            save(dataApiAuthEntity);
        } else {
            dataApiAuthEntity.setApproveStatus(DataApiApproveStatusEnum.WAIT_APPROVE.getApproveStatus());
            dataApiAuthEntity.setApplyTime(new Date());
            dataApiAuthEntity.setApiName(dataApiEntity.getApiName());
            dataApiAuthEntity.setUpdateTime(new Date());
            dataApiAuthEntity.setUsername(username);
            dataApiAuthEntity.setUpdaterId(userId);
            updateById(dataApiAuthEntity);
        }
        return true;
    }

    @Override
    public boolean applyApiByIds(String userId, List<Long> apiIds, Long projectId, String username) {
        List<DataApiEntity> dataApiEntities = dataApiService.listByIds(apiIds);
        List<DataApiAuthEntity> list = this.list(
            new LambdaQueryWrapper<>(DataApiAuthEntity.class).in(DataApiAuthEntity::getApiId, apiIds)
                .eq(DataApiAuthEntity::getUserId, userId));
        Map<Long, DataApiAuthEntity> authMap = list.stream()
            .collect(Collectors.toMap(e -> e.getApiId(), e -> e));
        Map<Long, DataApiEntity> apiMap = dataApiEntities.stream()
            .collect(Collectors.toMap(e -> e.getId(), e -> e));
        for (Long apiId : apiIds) {
            DataApiAuthEntity dataApiAuthEntity = authMap.get(apiId);
            // 不存在，新申请
            if (Objects.isNull(dataApiAuthEntity)) {
                DataApiEntity dataApiEntity = apiMap.get(apiId);
                dataApiAuthEntity = new DataApiAuthEntity();
                dataApiAuthEntity.setApiId(apiId);
                dataApiAuthEntity.setApiName(dataApiEntity.getApiName());
                dataApiAuthEntity.setApplyTime(new Date());
                dataApiAuthEntity.setUserId(userId);
                dataApiAuthEntity.setCreateTime(new Date());
                dataApiAuthEntity.setProjectId(projectId);
                dataApiAuthEntity.setApproveStatus(
                    DataApiApproveStatusEnum.WAIT_APPROVE.getApproveStatus());
                dataApiAuthEntity.setCreatorId(userId);
                dataApiAuthEntity.setUsername(username);
                save(dataApiAuthEntity);
            } else {
                // 已授权，待审批的api不需要再申请了
                if (!dataApiAuthEntity.getApproveStatus()
                    .equals(DataApiApproveStatusEnum.WAIT_APPROVE.getApproveStatus())
                    && !dataApiAuthEntity.getApproveStatus()
                    .equals(DataApiApproveStatusEnum.PASSED.getApproveStatus())) {
                    DataApiEntity dataApiEntity = apiMap.get(apiId);
                    dataApiAuthEntity.setApproveStatus(
                        DataApiApproveStatusEnum.WAIT_APPROVE.getApproveStatus());
                    dataApiAuthEntity.setApplyTime(new Date());
                    dataApiAuthEntity.setApiName(dataApiEntity.getApiName());
                    dataApiAuthEntity.setUpdateTime(new Date());
                    dataApiAuthEntity.setUsername(username);
                    dataApiAuthEntity.setUpdaterId(userId);
                    updateById(dataApiAuthEntity);
                }
            }
        }
        return false;
    }

    @Override
    public IPage<DataApiAuthApproveDomain> getApiAuthApprovePage(
        DataApiAuthApproveParam dataApiAuthApproveParam) {

        LambdaQueryWrapper<DataApiAuthEntity> lambdaQueryWrapper =
            new LambdaQueryWrapper<DataApiAuthEntity>()
                .eq(DataApiAuthEntity::getProjectId, dataApiAuthApproveParam.getProjectId());

        if (StringUtils.isNotBlank(dataApiAuthApproveParam.getSearchName())) {
            lambdaQueryWrapper.and(
                e ->
                    e.like(DataApiAuthEntity::getUsername, dataApiAuthApproveParam.getSearchName())
                        .or()
                        .like(DataApiAuthEntity::getApiName, dataApiAuthApproveParam.getSearchName()));
        }
        if (Objects.nonNull(dataApiAuthApproveParam.getApproveStatus())) {
            lambdaQueryWrapper.eq(
                DataApiAuthEntity::getApproveStatus, dataApiAuthApproveParam.getApproveStatus());
        }

        lambdaQueryWrapper.orderByDesc(DataApiAuthEntity::getApplyTime);

        IPage<DataApiAuthApproveDomain> dataApiAuthApproveDomainPageResult =
            page(
                new Page<>(
                    dataApiAuthApproveParam.getPageNum(), dataApiAuthApproveParam.getPageSize()),
                lambdaQueryWrapper)
                .convert(e -> Convert.convert(DataApiAuthApproveDomain.class, e));

        List<DataApiAuthApproveDomain> result = dataApiAuthApproveDomainPageResult.getRecords();
        result.forEach(
            e -> {
                UserDomain userById = userService.getUserById(e.getUserId());
                if (Objects.isNull(userById)) {
                    e.setUserName("匿名用户");
                } else {
                    e.setUserName(userById.getUserName());
                }

            });
        return dataApiAuthApproveDomainPageResult;
    }

    @Override
    public Boolean approveUserApiAuthById(DataApiAuthApproveOrderParam dataApiAuthApproveOrderParam) {

        DataApiAuthEntity dataApiAuthEntity = getById(dataApiAuthApproveOrderParam.getId());
        if (dataApiAuthEntity == null
            || !dataApiAuthEntity
            .getApproveStatus()
            .equals(DataApiApproveStatusEnum.WAIT_APPROVE.getApproveStatus())) {
            throw new BusinessException("该申请并非处于待审批状态,审批失败");
        }

        String userId = dataApiAuthEntity.getUserId();
        Long apiId = dataApiAuthEntity.getApiId();

        // 判断该API的状态是否发生改变
        DataApiEntity dataApiEntity = dataApiService.getById(apiId);
        if (null == dataApiEntity || dataApiEntity.getApiStatus() == 0) {
            throw new BusinessException("该API不存在或者未发布,无法审核");
        }
        dataApiAuthEntity.setApproveReason(dataApiAuthApproveOrderParam.getApproveReason());

        if (dataApiAuthApproveOrderParam.getApproveOrder().equals(0)) {
            dataApiAuthEntity.setApproveStatus(DataApiApproveStatusEnum.REDUSED.getApproveStatus());
            dataApiAuthEntity.setAuditorId(dataApiAuthApproveOrderParam.getUserId());
            this.updateById(dataApiAuthEntity);
        } else {
            dataApiAuthEntity.setApproveStatus(DataApiApproveStatusEnum.PASSED.getApproveStatus());

            Integer authType = dataApiEntity.getAuthType();
            DataApiTokenDomain dataApiTokenDomain = new DataApiTokenDomain();
            dataApiTokenDomain.setApiId(apiId);
            dataApiTokenDomain.setIsSecurity(dataApiEntity.getIsSecurity());
            dataApiTokenDomain.setUserId(userId);
            dataApiTokenDomain.setIsProd(true);
            String value = JSONObject.toJSONString(dataApiTokenDomain);
            // api token
            if (Objects.isNull(authType) || authType == 0) {
                String apiToken = dataApiAuthEntity.getApiToken();
                if (StringUtils.isBlank(apiToken)) {
                    apiToken = UUID.randomUUID().toString().replaceAll("-", "");
                    dataApiAuthEntity.setApiToken(apiToken);
                }
                String key =
                    new StringBuilder(API_REDIS_KEY_PREFIX)
                        .append("token")
                        .append("_")
                        .append(apiToken)
                        .toString();
                redisTemplate.opsForValue().set(key, value);
            } else {
                UserDomain userDomain = userService.getUserById(dataApiAuthEntity.getUserId());
                String userToken = tokenService.getToken(userDomain.getId());
                userToken = API_REDIS_KEY_PREFIX + "token_" + userToken + dataApiEntity.getApiPath();
                redisTemplate.opsForValue().set(userToken, value);
            }
            dataApiAuthEntity.setAuditorId(dataApiAuthApproveOrderParam.getUserId());
            this.updateById(dataApiAuthEntity);

            LambdaQueryWrapper<DataApiUserStatisticsEntity> eq =
                new LambdaQueryWrapper<DataApiUserStatisticsEntity>()
                    .eq(DataApiUserStatisticsEntity::getUserId, userId)
                    .eq(DataApiUserStatisticsEntity::getApiId, apiId);

            DataApiUserStatisticsEntity one = dataApiUserStatisticsService.getOne(eq, false);

            // 如果未查询用该用户的记录数，插入一条记录
            if (null == one) {
                DataApiUserStatisticsEntity dataApiUserStatistics = new DataApiUserStatisticsEntity();
                dataApiUserStatistics.setApiId(apiId);
                dataApiUserStatistics.setCreateTime(new Date());
                dataApiUserStatistics.setProjectId(dataApiAuthApproveOrderParam.getProjectId());
                dataApiUserStatistics.setUpdateTime(new Date());
                dataApiUserStatistics.setUserId(userId);
                dataApiUserStatistics.setTotalCount(0L);
                dataApiUserStatistics.setSuccCount(0L);
                dataApiUserStatistics.setFailCount(0L);
                dataApiUserStatistics.setUsername(dataApiAuthEntity.getUsername());
                dataApiUserStatisticsService.save(dataApiUserStatistics);
            }
        }

        return true;
    }

    @Override
    public Boolean cancelUserApiAuthById(Long id, Integer status) {
        DataApiAuthEntity dataApiAuthEntity = getById(id);
        Long apiId = dataApiAuthEntity.getApiId();
        DataApiDomain dataApiById = dataApiService.findDataApiById(apiId);
        UserDomain userById = userService.getUserById(dataApiAuthEntity.getUserId());
        if (dataApiAuthEntity == null) {
            throw new BusinessException("无法取消审批");
        }
        if (status.equals(DataApiApproveStatusEnum.CANCELD.getApproveStatus())) {
            String key = API_REDIS_KEY_PREFIX + "token" + "_" + dataApiAuthEntity.getApiToken();
            String key2 =
                API_REDIS_KEY_PREFIX + "token" + "_" + tokenService.getToken(userById.getId()) + dataApiById.getApiPath();
            dataApiRestService.removeConfigCache(apiId, dataApiById.getApiPath());
            redisTemplate.delete(key);
            redisTemplate.delete(key2);
        }
        dataApiAuthEntity.setUpdateTime(new Date());
        dataApiAuthEntity.setUpdaterId(UserContext.getUserId());
        dataApiAuthEntity.setApproveStatus(status);
        this.updateById(dataApiAuthEntity);
//        LogContext.put(LogKey.NAME.getKey(), dataApiById.getApiName());
        return true;
    }

    @Override
    public DataApiAuthDomain findAuthDomainByUserIdAndApiId(String userId, Long apiId) {
        LambdaQueryWrapper<DataApiAuthEntity> eq =
            new LambdaQueryWrapper<DataApiAuthEntity>()
                .eq(DataApiAuthEntity::getUserId, userId)
                .eq(DataApiAuthEntity::getApiId, apiId)
                .eq(DataApiAuthEntity::getProjectId, ServletUtils.getProjectId());
        DataApiAuthEntity one = getOne(eq);
        DataApiAuthDomain dataApiAuthDomain = ConvertUtil.copyProperties(one, DataApiAuthDomain.class);
        return dataApiAuthDomain;
    }

    @Override
    public DataApiTokenDomain getApiIdByToken(String token, String apiPath) {
        String tokenKey = API_REDIS_KEY_PREFIX + "token_" + token;

        String userTokenKey = API_REDIS_KEY_PREFIX + "token_" + token + apiPath;

        String v1 = redisTemplate.opsForValue().get(tokenKey);

        String v2 = redisTemplate.opsForValue().get(userTokenKey);

        if (StringUtils.isNotBlank(v2)) {
            return JSON.parseObject(v2, DataApiTokenDomain.class);
        }
        if (StringUtils.isNotBlank(v1)) {
            return JSON.parseObject(v1, DataApiTokenDomain.class);
        }

        // 可能是测试api
        String testToken = API_REDIS_KEY_PREFIX + "test_token_" + token + apiPath;
        String testValue = redisTemplate.opsForValue().get(testToken);
        if (StringUtils.isNotBlank(testValue)) {
            return JSON.parseObject(testValue, DataApiTokenDomain.class);
        }

        // 测试api失效会刷新正式api，不会删除其它的测试api
        if (StringUtils.isBlank(v2) && StringUtils.isBlank(v1)) {
            this.initProdApiToRedis();
        }

        String v11 = redisTemplate.opsForValue().get(tokenKey);

        String v22 = redisTemplate.opsForValue().get(userTokenKey);

        if (StringUtils.isNotBlank(v22)) {
            return JSON.parseObject(v22, DataApiTokenDomain.class);
        }
        if (StringUtils.isNotBlank(v11)) {
            return JSON.parseObject(v11, DataApiTokenDomain.class);
        }
        return null;
    }

    @Override
    public boolean removeDataApiAuthByApiId(Long id) {
        LambdaQueryWrapper<DataApiAuthEntity> lambdaQueryWrapper =
            new LambdaQueryWrapper<DataApiAuthEntity>().eq(DataApiAuthEntity::getApiId, id);
        this.remove(lambdaQueryWrapper);
        return true;
    }

    @Override
    public List<DataApiAuthEntity> getDataApiAuthByIds(List<Long> ids, String userId, Long projectId) {

        LambdaQueryWrapper<DataApiAuthEntity> in =
            new LambdaQueryWrapper<DataApiAuthEntity>()
                .eq(DataApiAuthEntity::getUserId, userId)
                .eq(DataApiAuthEntity::getProjectId, projectId)
                .in(DataApiAuthEntity::getApiId, ids);

        List<DataApiAuthEntity> res = this.list(in);
        return res;
    }

    @Override
    public boolean deleteApiApproveById(Long id) {
        this.removeById(id);
        return true;
    }

    @Override
    public boolean applyApiByAuthId(Long id) {
        DataApiAuthEntity dataApiAuthEntity = this.getById(id);
        dataApiAuthEntity.setApproveStatus(DataApiApproveStatusEnum.WAIT_APPROVE.getApproveStatus());
        dataApiAuthEntity.setApplyTime(new Date());
        dataApiAuthEntity.setUpdateTime(new Date());
        String userId = UserContext.getUserId();
        dataApiAuthEntity.setUpdaterId(userId);
        this.updateById(dataApiAuthEntity);
//        LogContext.put(LogKey.NAME.getKey(), dataApiAuthEntity.getApiName());
        return true;
    }

    @Override
    public void initProdApiToRedis() {
        String tokenPattern = API_REDIS_KEY_PREFIX + "token_*";
        Set<String> keys = redisTemplate.keys(tokenPattern);
        redisTemplate.delete(keys);

        LambdaQueryWrapper<DataApiAuthEntity> lambdaQueryWrapper =
            new LambdaQueryWrapper<DataApiAuthEntity>()
                .eq(
                    DataApiAuthEntity::getApproveStatus,
                    DataApiApproveStatusEnum.PASSED.getApproveStatus());
        List<DataApiAuthEntity> authEntities = this.list(lambdaQueryWrapper);

        List<Long> apiIds = authEntities.stream().map(e -> e.getApiId()).collect(Collectors.toList());

        Map<Long, DataApiEntity> apiPathMap = dataApiService.getApiPathMap(apiIds);
        if (MapUtils.isEmpty(apiPathMap)) {
            return;
        }
        for (DataApiAuthEntity authEntity : authEntities) {
            Long apiId = authEntity.getApiId();
            DataApiEntity dataApiEntity = apiPathMap.get(apiId);
            DataApiTokenDomain domain = new DataApiTokenDomain();
            domain.setIsProd(true);
            domain.setApiId(authEntity.getApiId());
            domain.setUserId(authEntity.getUserId());

            // todo api 异常缺少待排查（可能是迁移程序操作数据库导致），先处理空指针
            if (Objects.isNull(dataApiEntity)) {
                continue;
            }
            domain.setIsSecurity(dataApiEntity.getIsSecurity());
            // api token
            if (Objects.isNull(dataApiEntity.getAuthType()) || dataApiEntity.getAuthType().equals(0)) {
                domain.setUserId(null);
                String tokenKey = API_REDIS_KEY_PREFIX + "token_" + authEntity.getApiToken();
                redisTemplate.opsForValue().set(tokenKey, JSON.toJSONString(domain));
            } else {
                UserDomain userById = userService.getUserById(authEntity.getUserId());
                String userToken = tokenService.getToken(userById.getId());
                String tokenKey = API_REDIS_KEY_PREFIX + "token_" + userToken + dataApiEntity.getApiPath();
                redisTemplate.opsForValue().set(tokenKey, JSON.toJSONString(domain));
            }
        }
    }

    @Override
    public boolean exitUsingApiByApiId(Long id) {
        LambdaQueryWrapper<DataApiAuthEntity> eq =
            new LambdaQueryWrapper<DataApiAuthEntity>()
                .eq(DataApiAuthEntity::getApiId, id)
                .in(
                    DataApiAuthEntity::getApproveStatus,
                    Lists.newArrayList(
                        DataApiApproveStatusEnum.PASSED.getApproveStatus(),
                        DataApiApproveStatusEnum.WAIT_APPROVE.getApproveStatus()));

        long count = this.count(eq);
        boolean flag = count > 0;
        return flag;
    }

    @Override
    public void updateApiAuthName(Long apiId, String newName) {

        LambdaQueryWrapper<DataApiAuthEntity> eq =
            new LambdaQueryWrapper<DataApiAuthEntity>().eq(DataApiAuthEntity::getApiId, apiId);
        List<DataApiAuthEntity> list = list(eq);
        list.forEach(
            e -> {
                e.setApiName(newName);
            });
        this.updateBatchById(list);
    }

    @Override
    public IPage<DataApiMyApplyDomain> getMyApplyApiPage(
        Integer approveStatus,
        String userId,
        Long projectId,
        Integer pageNum,
        Integer pageSize,
        String apiName) {

        LambdaQueryWrapper<DataApiAuthEntity> eq =
            new LambdaQueryWrapper<DataApiAuthEntity>()
                .eq(DataApiAuthEntity::getUserId, userId)
                .eq(DataApiAuthEntity::getProjectId, projectId);
        if (!Objects.isNull(approveStatus)) {
            eq.eq(DataApiAuthEntity::getApproveStatus, approveStatus);
        }
        if (StringUtils.isNotBlank(apiName)) {
            eq.like(DataApiAuthEntity::getApiName, apiName);
        }
        eq.orderByDesc(DataApiAuthEntity::getApplyTime);

        IPage<DataApiMyApplyDomain> convert =
            page(new Page<>(pageNum, pageSize), eq)
                .convert(e -> Convert.convert(DataApiMyApplyDomain.class, e));
        return convert;
    }
}
